package com.sweet.multipledatasources;

import com.sweet.multipledatasources.api.ApiVerticle;
import com.sweet.multipledatasources.db.DbVerticle;
import io.vertx.config.ConfigRetriever;
import io.vertx.config.ConfigRetrieverOptions;
import io.vertx.config.ConfigStoreOptions;
import io.vertx.core.AbstractVerticle;
import io.vertx.core.DeploymentOptions;
import io.vertx.core.Promise;
import io.vertx.core.Vertx;
import io.vertx.core.impl.logging.Logger;
import io.vertx.core.impl.logging.LoggerFactory;
import io.vertx.core.json.JsonObject;

import java.util.HashMap;
import java.util.Map;


public class MainVerticle extends AbstractVerticle {

  private static final String CONFIG_FILE = "config.json";

  private final Logger logger = LoggerFactory.getLogger(MainVerticle.class);

  public static void main(String[] args) {
    Vertx.vertx().deployVerticle(new MainVerticle());
  }

  private String apiServiceDeployId = null;
  private Map<String, String> dbServiceDeployIdMap = new HashMap<>();

  @Override
  public void start(Promise<Void> startPromise) throws Exception {
    ConfigRetriever retriever = getConfigRetriever();
    retriever.getConfig().onSuccess(res -> {
      JsonObject apiConfig = res.getJsonObject("api");
      JsonObject dbConfig = res.getJsonObject("db");
      deployApiService(apiConfig);
      deployDbService(dbConfig);
    }).onFailure(fail -> logger.error("configErr", fail));

    retriever.listen(config -> {
      JsonObject oldConfig = config.getPreviousConfiguration();
      JsonObject newConfig = config.getNewConfiguration();
      logger.debug("oldConfig: " + oldConfig);
      logger.debug("newConfig: " + newConfig);

      restartApiService(oldConfig, newConfig);
      restartDbService(oldConfig, newConfig);
    });
    startPromise.complete();
  }

  private void deployDbService(JsonObject dbConfig) {
    for (Map.Entry<String, Object> entry : dbConfig) {
      String dbName = entry.getKey();
      JsonObject configJson = (JsonObject) entry.getValue();
      vertx.deployVerticle(new DbVerticle(), new DeploymentOptions().setWorker(true)
                      .setConfig(configJson.copy().put("name", dbName)))
              .onFailure(fail -> {
                logger.error("[" + dbName + "]DbServiceDeployErr", fail);
              }).onSuccess(dbRes -> {
                logger.debug("[" + dbName + "]DbServiceDeploySuccess: " + dbRes);
                dbServiceDeployIdMap.put(dbName, dbRes);
              });
    }
  }

  private void deployApiService(JsonObject apiConfig) {
    vertx.deployVerticle(new ApiVerticle(), new DeploymentOptions().setConfig(apiConfig))
            .onFailure(fail -> {
              logger.error("ApiServiceDeployErr", fail);
            }).onSuccess(apiRes -> {
              logger.debug("ApiServiceDeploySuccess: " + apiRes);
              apiServiceDeployId = apiRes;
            });
  }

  private void restartApiService(JsonObject oldConfig, JsonObject newConfig) {
    JsonObject oldApiConfig = oldConfig.getJsonObject("api");
    JsonObject newApiConfig = newConfig.getJsonObject("api");
    if (!oldApiConfig.equals(newApiConfig)) {
      logger.debug("apiConfigChange");
      vertx.undeploy(apiServiceDeployId)
              .compose(a -> vertx.deployVerticle(new ApiVerticle(), new DeploymentOptions()
                      .setConfig(newApiConfig))).onSuccess(res -> {
                logger.debug("restartApiServiceSuccess: " + res);
                apiServiceDeployId = res;
              }).onFailure(err -> {
                logger.error("restartApiServiceErr", err);
              });
    }
  }

  private void restartDbService(JsonObject oldConfig, JsonObject newConfig) {
    JsonObject oldDbConfig = oldConfig.getJsonObject("db");
    JsonObject newDbConfig = newConfig.getJsonObject("db");
    if (!oldDbConfig.equals(newDbConfig)) {
      logger.debug("dbConfigChange");
      logger.debug("oldConfig:" + oldDbConfig);
      logger.debug("newConfig:" + newDbConfig);
      for (Map.Entry<String, String> entry : new HashMap<>(dbServiceDeployIdMap).entrySet()) {
        String dbName = entry.getKey();
        String dbServiceDeployId = entry.getValue();
        JsonObject dbConfigJson = newDbConfig.getJsonObject(dbName);
        vertx.undeploy(dbServiceDeployId)
                .compose(a -> vertx.deployVerticle(new DbVerticle(), new DeploymentOptions()
                        .setConfig(dbConfigJson.copy().put("name", dbName)))).onSuccess(res -> {
                  logger.debug("[" + dbName + "]restartDbServiceSuccess: " + res);
                  dbServiceDeployIdMap.put(dbName, res);
                }).onFailure(err -> {
                  logger.error("[" + dbName + "]restartDbServiceErr", err);
                });
      }
    }
  }

  private ConfigRetriever getConfigRetriever() {
    ConfigStoreOptions fileStore = new ConfigStoreOptions()
            .setType("file")
            .setConfig(new JsonObject().put("path", CONFIG_FILE));
    ConfigRetrieverOptions options = new ConfigRetrieverOptions()
            .addStore(fileStore);
    return ConfigRetriever.create(vertx, options);
  }
}
